import type { interForm, interTable } from "@b/model/base/type";
import { Http } from "@b/model/base/http";
import { Plus } from "@element-plus/icons-vue";
import { ElSelect, ElOption, ElSwitch, ElInputNumber, ElUpload, ElIcon, type UploadUserFile, ElInput, ElImage, ElRadioGroup, ElRadio, ElCascader, ElDatePicker, ElCheckbox, ElCheckboxGroup } from "element-plus";
import { h, isVNode } from "vue";
import B_upload from "../widget/b_upload.vue";

export class FormRender<T extends Record<string, any>, U extends keyof T & string> extends Http {
    filelist: UploadUserFile[];
    constructor() {
        super()
        this.filelist = []
    }
    render(
        data: T,
        item: interForm<T, U>,
    ) {
        const { label, clearable = false, placeholder, prop, type } = item
        const defaultValue = item.inpDefault && item.inpDefault(data)
        const value = data[prop as U]
        let inpDisabled = item.inpDisabled
        if (!data.id) inpDisabled = false
        const InputChange = (data: T, prop: U, value: any, it?: any) => {
            item.onChange && item.onChange({ value, data, item: it })
            data[prop as U] = value as any
        }
        if ([undefined, null, ''].includes(value) && !this.utils_isEmpty(defaultValue)) {
            data[prop as U] = defaultValue as any
            return
        }
        switch (type) {
            case 'radio':
                return h(ElRadioGroup, {
                    modelValue: value as string,
                    disabled: inpDisabled,
                    'onUpdate:modelValue': (value) => InputChange(data, prop as U, value),
                }, () => item.options?.map(option => h(ElRadio, {
                    label: option.label,
                    value: option.value
                })));
            case 'checkbox':
                return h(ElCheckboxGroup, {
                    modelValue: value,
                    disabled: inpDisabled || false,
                    'onUpdate:modelValue': (value) => InputChange(data, prop as U, value),
                }, () => item.options?.map(option => h(ElCheckbox, {
                    label: option.label,
                    value: option.value
                })));
            case 'select':
                if (!item.options) return h('div')
                return h(ElSelect, {
                    modelValue: value as string, multiple: item.multiple || false,
                    filterable: item.inputable, allowCreate: item.inputable,
                    placeholder: placeholder || '请选择' + label, clearable,
                    'onUpdate:modelValue': (value) => InputChange(data, prop as U, value, item.options?.find(it => it.value === value))
                }, {
                    default: () => item.options?.map(option => h(ElOption, { label: option.label, value: option.value }))
                });
            case 'cascader': {
                if (!item.options) return h('div')
                return h(ElCascader, {
                    modelValue: value as string, options: item.options,
                    props: { ...item.props, checkStrictly: item.checkStrictly || false, emitPath: false, multiple: item.multiple || false },
                    placeholder: placeholder || '请选择' + label, clearable,
                    'onUpdate:modelValue': (value) => InputChange(data, prop as U, value, item.options?.find(it => it.value === value))
                });
            }
            case 'bool':
                return h(ElSwitch, {
                    modelValue: Boolean(value), disabled: inpDisabled,
                    'onUpdate:modelValue': (value) => InputChange(data, prop as U, value)
                });
            case 'number':
                return h(ElInputNumber, {
                    modelValue: value as number, clearable, disabled: inpDisabled, max: item.max, min: item.min,
                    'onUpdate:modelValue': (value) => InputChange(data, prop as U, value),
                    placeholder: placeholder || '请输入' + label
                });
            case 'image':
                const size = computedWidth(item.formSize)
                return h(B_upload, {
                    value,
                    size, accept: 'image/*', onChange: async (fileUrl) => {
                        if (item.after) return item.after(fileUrl || '')
                        InputChange(data, prop as U, fileUrl)
                    }, onRemove: () => {
                        InputChange(data, prop as U, '')
                    }
                })
            case 'images':
                this.filelist = (value as string[] || []).map(item => ({ url: item, name: '' }))
                return h('div', {}, h(ElUpload, {
                    class: 'avatar', fileList: this.filelist, multiple: true, listType: 'picture-card',
                    autoUpload: false, accept: 'image/*', onChange: async (file, files) => {
                        InputChange(data, prop as U, files.map(item => item.url || '') as any)
                    }, onRemove: (file, files) => {
                        InputChange(data, prop as U, files.map(item => item.url || '') as any)
                    }
                }, { default: () => h(ElIcon, {}, { default: () => h(Plus) }) }))
            case 'text':
                return h(ElInput, {
                    modelValue: value as string,
                    'onUpdate:modelValue': (value) => InputChange(data, prop as U, value),
                    onKeydown: (e: any) => {
                        if (!item.keyEnter) return
                        if (e.key === 'Enter' || e.key === 'NumpadEnter') {
                            item.keyEnter()
                        }
                    },
                    placeholder: placeholder || '请输入' + label,
                    clearable,
                    disabled: inpDisabled,
                    type: 'text'
                });
            case 'textarea':
                return h(ElInput, {
                    modelValue: value as string,
                    'onUpdate:modelValue': (value) => InputChange(data, prop as U, value),
                    onKeydown: (e: any) => {
                        if (!item.keyEnter) return
                        if (e.key === 'Enter' || e.key === 'NumpadEnter') {
                            item.keyEnter()
                        }
                    },
                    placeholder: placeholder || '请输入' + label,
                    clearable, rows: item.rows || 2,
                    maxlength: item.maxlength,
                    showWordLimit: true,
                    disabled: inpDisabled,
                    type: 'textarea'
                });
            case 'password':
                return h(ElInput, {
                    modelValue: value as string,
                    'onUpdate:modelValue': (value) => InputChange(data, prop as U, value),
                    onKeydown: (e: any) => {
                        if (!item.keyEnter) return
                        if (e.key === 'Enter' || e.key === 'NumpadEnter') {
                            item.keyEnter()
                        }
                    },
                    placeholder: placeholder || '请输入' + label,
                    clearable,
                    disabled: inpDisabled,
                    type: 'password'
                });
            case 'date':
                return h(ElDatePicker, {
                    modelValue: value as string,
                    'onUpdate:modelValue': (value) => InputChange(data, prop as U, value),
                    placeholder: placeholder || '请输入' + label,
                    startPlaceholder: '请输入开始时间',
                    endPlaceholder: '请输入结束时间',
                    clearable,
                    valueFormat: item.format,
                    disabled: inpDisabled,
                    type: item.dateType || 'date'
                });
            default:
                return null;
        }
    }
}

export class DetailRender<T extends Record<string, any>, U extends keyof T & string> extends Http {
    render(
        data: T,
        item: interForm<T, U>
    ) {
        const value = data[item.prop as U];
        if (item.type === 'image') {
            // 这是一个bug，需要新加一个详情页的图片尺寸（不能用现有的宽/高）不想加了，先用着吧
            const size = computedWidth(item.formSize);
            return h("img", {
                src: value as string,
                style: {
                    width: size,
                    height: size
                }
            })
        } else if (item.type === 'password') {
            return '******'
        } else if (item.type === 'images') {
            // 这是一个bug，需要新加一个详情页的图片尺寸（不能用现有的宽/高）不想加了，先用着吧
            const size = computedWidth(item.formSize);
            return h("div", {}, value?.map((v: string) => h("img", {
                src: v,
                style: {
                    width: size,
                    height: size,
                    marginRight: '12px',
                    marginBottom: '12px'
                }
            })));
        }
        else if (item.type === 'bool') {
            return h("div", {}, value ? item.trueText || '是' : item.falseText || '否');
        } else if (item.type === 'select' || item.type === 'radio' || item.type === 'cascader') {
            const selectName = item.selectName
            if (Array.isArray(value)) {
                return h("div", {}, value.map((v: string) => {
                    const it = item.options?.find(it => it.value === v)
                    return it?.label || v;
                }
                ).join(','));
            }
            return h("div", {}, selectName ? data[selectName as U] || item.default : item.options?.find(item => item.value === value)?.label || value || item.default);
        }
        return h("div", {}, value || item.default);
    };
}

export class TableRender<T extends Record<string, any>, U extends keyof T & string> extends Http {
    render(
        data: T,
        item: interTable<T, U>,
        index: number,
    ) {
        const { prop, type } = item
        const value = data[prop as U];
        if (type === 'customize') {
            if (!item.formatter) return h("div");
            const v = item.formatter({ prop: prop as U, data, value, index });
            if (isVNode(v)) {
                return v;
            } else {
                return h("span", {}, v);
            }
        } else if (type === 'image') {
            const size = computedWidth(item.size);
            return h(ElImage, {
                src: value as string,
                style: {
                    width: size,
                    height: size,
                }
            });
        } else if (type === 'images') {
            const size = computedWidth(item.size);
            if (value && value !== 'null') {
                let val = value
                if (typeof value === 'string')
                    val = JSON.parse(value)
                return h("div", {
                    style: { display: 'flex', flexWrap: 'wrap' },
                }, (val as string[]).slice(0, item.maxShow).map((v) => h(ElImage, {
                    src: v,
                    style: {
                        width: size,
                        height: size,
                        marginRight: '10px',
                        marginBottom: '10px',
                    }
                })
                ))
            }
            else
                return h("div", {}, item.default)
        } else if (type === 'bool') {
            return h("span", {}, value ? item.trueText || '是' : item.falseText || '否');
        } else if (type === 'switch') {
            return h(ElSwitch, {
                modelValue: Boolean(value),
                loading: item.loading,
                "onUpdate:modelValue": async (value: any) => {
                    item.loading = true;
                    if (item.onChange) await item.onChange({ prop, value, data, index })
                    data[prop] = value
                    item.loading = false;
                }
            })
        } else if (type === 'select') {
            if (item.selectName) return h("span", {}, data[item.selectName as U] || item.default)
            if (!item.options) return h("span", {}, JSON.stringify(value) || item.default)
            let val = value
            if (typeof value === 'string' && item.multiple)
                val = JSON.parse(value)
            return h("span", {},
                Array.isArray(val) ? val.map((v: any) => item.options?.find(v1 => v1.value === v)?.label || item.default).join(',') :
                    item.options?.find(v => v.value === val)?.label || item.default);
        } else if (type === 'radio') {
            return h(ElRadioGroup, {
                modelValue: value as string,
                'onUpdate:modelValue': async (value: any) => {
                    data[prop] = value
                    if (item.onChange)
                        await item.onChange({ prop, value, data, index })
                },
            }, () => item.options?.map(option => h(ElRadio, {
                label: option.label,
                value: option.value
            })));
        }
        else if (type === "json") {
            if (value && value !== 'null') {
                return h("pre", {}, JSON.stringify(value, null, 2));
            } else {
                return h("span", {}, item.default);
            }
        }
        else {
            return h("span", {}, value ? value.toString() : item.default);
        }
    };
}

export const computedWidth = (width?: string | number) => typeof width === 'number'
    ? width + 'px'
    : width;